package nonfixcycle

import (
	"blockchain/public/entity"
	"blockchain/public/function"
	"bytes"
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"math"
)
const N=entity.N //节点数
const K=entity.K //主节点数
const BlockSize=entity.BlockSize
const TradeSize=entity.TradeSize
var sumrounds=entity.SumRounds
const alpha = 2
const prior =0.4
const clientSeq=499
//var currentRound=0
var currentTradeSeq=0
var trades [20000]entity.Trade
var node [N] entity.Node
var primarysSeq [K]int
var replicasSeq [N-K]int
var initialNode=false
var TradeSet [N][5][K*TradeSize] entity.Trade
var chanPriRecRequest=make([]chan requestMessage,N)
var chanPriRecRequest2=make([]chan requestMessage,N)

var chanNodeRecPre_prepare=make([]chan pre_prepareMessage,N)
var FlagPre_prepared=make([][]chan bool,N,N)
var chanPriRecPrepare1=make([]chan prepare1Message,N)
var FlagPrepared1=make([][]chan bool,N,N)
var chanNodeRecPrepare2=make([]chan prepare2Message,N)
var FlagPrepared2=make([][]chan bool,N,N)
var chanPriRecCommit1=make([]chan commit1Message,N)
var FlagCommitted1=make([][]chan bool,N,N)
var chanNodeRecCommit2=make([]chan commit2Message,N)
var FlagCommitted2=make([][]chan bool,N,N)


var chanT135ToNode =make([]chan messageT135,N)
var FlagT135ed=make([][]chan bool,N,N)
var chanT24ToPri=make([]chan messageT24,N)
var FlagT24ed=make([][]chan bool,N,N)
var chanT135ToNode_1 =make([]chan messageT135,N)
var FlagT135_1ed=make([][]chan bool,N,N)
var chanT24ToPri_1=make([]chan messageT24,N)
var FlagT24_1ed=make([][]chan bool,N,N)
var chanReply=make(chan commit2Message,K)
var FlagReplied=make([]chan bool,N)
var chanReply_1=make(chan commit2Message,K)
var FlagReplied_1=make([]chan bool,N)
var blockchain[1000] entity.Block

var flagAllDone=make(chan bool)

func Main(){
	fmt.Printf("多阶段消息统一签名、无固定周期流水线共识:N=%d,K=%d,R=%d",N,K,sumrounds)
	currentTradeSeq=0
	channelInitial()
	 currentRound:=0
	 currentRound_odd:=0
	flagAllDone=make(chan bool)
	go func() {
		for currentRound=0;currentRound<sumrounds+4;currentRound+=2{
			var T1,T2,T3,T4,T5 int
			//fmt.Printf("第%d轮共识：\n",currentRound)
			if currentRound%2==0{
				request(currentRound)
			}else {
				request1(currentRound)
			}
			T5=currentRound//T5对应流水线途中phase1
			T4=T5-1
			T3=T4-1
			T2=T3-1
			T1=T2-1

			var flagPri=make([]chan bool,N)
			var flagNode=make([]chan bool,N)

			for i:=range primarysSeq{
				flagPri[primarysSeq[i]]=make(chan bool,1)
				go primaryConsensus(primarysSeq[i],flagPri[primarysSeq[i]],T5,T3,T1)
			}
			for i:=0;i<N;i++{
				flagNode[i]=make(chan bool,1)
				go replicaConsensus(i,flagNode[i],T4,T2)
			}

			for i:=range primarysSeq {
				<-flagPri[primarysSeq[i]]
			}
			for i:=0;i<N;i++ {
				<-flagNode[i]
			}
			if T1>=0{
				if T1%2==0{
					reply(T1)
				}else {
					reply1(T1)
				}
			}

			if T5==sumrounds+3{
				flagAllDone<-true
				close(flagAllDone)
			}
		}
	}()

	go func() {
		for currentRound_odd=1;currentRound_odd<sumrounds+4;currentRound_odd+=2{
			var T1,T2,T3,T4,T5 int
			//fmt.Printf("第%d轮共识：\n",currentRound)
			if currentRound_odd%2==0{
				request(currentRound_odd)
			}else {
				request1(currentRound_odd)
			}
			T5=currentRound_odd
			T4=T5-1
			T3=T4-1
			T2=T3-1
			T1=T2-1

			var flagPri=make([]chan bool,N)
			var flagNode=make([]chan bool,N)

			for i:=range primarysSeq{
				flagPri[primarysSeq[i]]=make(chan bool,1)
				go primaryConsensus(primarysSeq[i],flagPri[primarysSeq[i]],T5,T3,T1)
			}
			for i:=0;i<N;i++{
				flagNode[i]=make(chan bool,1)
				go replicaConsensus(i,flagNode[i],T4,T2)
			}

			for i:=range primarysSeq {
				<-flagPri[primarysSeq[i]]
			}
			for i:=0;i<N;i++ {
				<-flagNode[i]
			}
			if T1>=0{
				if T1%2==0{
					reply(T1)
				}else {
					reply1(T1)
				}
			}

			if T5==sumrounds+3{
				flagAllDone<-true
				close(flagAllDone)
			}
		}
	}()

	<-flagAllDone

}



type messageT135 struct {
	pre_prepare pre_prepareMessage
	prepare2 prepare2Message
	commit2 commit2Message
	sign string
	nodeSeq int
}

type messageT24 struct {
	prepare1 prepare1Message
	commit1 commit1Message
	sign string
	nodeSeq int
}


func sendT135Msg(nodeSeq int,t135 *messageT135){
	for i:=0;i<N;i++{
		select {
		case chanT135ToNode[i]<-*t135:
			FlagT135ed[nodeSeq][i]<-true
		default:
			FlagT135ed[nodeSeq][i]<-false
		}
	}
}

func sendT135Msg1(nodeSeq int,t135 *messageT135){
	for i:=0;i<N;i++{
		select {
		case chanT135ToNode_1[i]<-*t135:
			FlagT135_1ed[nodeSeq][i]<-true
		default:
			FlagT135_1ed[nodeSeq][i]<-false
		}
	}
}

func sendT24Msg(nodeSeq int,t24 *messageT24){
	for i:=0;i<K;i++{
		select {
		case chanT24ToPri[primarysSeq[i]]<-*t24:
			FlagT24ed[nodeSeq][primarysSeq[i]]<-true
		default:
			FlagT24ed[nodeSeq][primarysSeq[i]]<-false
		}
	}
}
func sendT24Msg1(nodeSeq int,t24 *messageT24){
	for i:=0;i<K;i++{
		select {
		case chanT24ToPri_1[primarysSeq[i]]<-*t24:
			FlagT24_1ed[nodeSeq][primarysSeq[i]]<-true
		default:
			FlagT24_1ed[nodeSeq][primarysSeq[i]]<-false
		}
	}
}

func getT135Msg(nodeSeq int,pre_prepareMsg *[K]pre_prepareMessage,prepare2Msg *[K]prepare2Message,commit2Msg *[K]commit2Message){
	count :=0
	for dat:=range chanT135ToNode[nodeSeq]{
		var data=make([][]byte,3)
		data[0],_=json.Marshal(dat.pre_prepare)
		data[1],_=json.Marshal(dat.prepare2)
		data[2],_=json.Marshal(dat.commit2)
		b:=bytes.Join(data,[]byte(""))
		res:=function.VerifySignHash(sha256.Sum256(b),dat.sign,dat.nodeSeq)
		if res{
			pre_prepareMsg[count]=dat.pre_prepare
			prepare2Msg[count]=dat.prepare2
			commit2Msg[count]=dat.commit2
			count++
		}
	}
	chanT135ToNode[nodeSeq]=make(chan messageT135,K)
}

func getT135Msg1(nodeSeq int,pre_prepareMsg *[K]pre_prepareMessage,prepare2Msg *[K]prepare2Message,commit2Msg *[K]commit2Message){
	count :=0
	for dat:=range chanT135ToNode_1[nodeSeq]{
		var data=make([][]byte,3)
		data[0],_=json.Marshal(dat.pre_prepare)
		data[1],_=json.Marshal(dat.prepare2)
		data[2],_=json.Marshal(dat.commit2)
		b:=bytes.Join(data,[]byte(""))
		res:=function.VerifySignHash(sha256.Sum256(b),dat.sign,dat.nodeSeq)
		if res{
			pre_prepareMsg[count]=dat.pre_prepare
			prepare2Msg[count]=dat.prepare2
			commit2Msg[count]=dat.commit2
			count++
		}
	}
	chanT135ToNode_1[nodeSeq]=make(chan messageT135,K)
}

func getT24Msg(nodeSeq int,t24Msg *[2*N/3+1]messageT24){
	count :=0
	for dat:=range chanT24ToPri[nodeSeq]{
		var data=make([][]byte,2)
		data[0],_=json.Marshal(dat.prepare1)
		data[1],_=json.Marshal(dat.commit1)
		b:=bytes.Join(data,[]byte(""))
		res:=function.VerifySignHash(sha256.Sum256(b),dat.sign,dat.nodeSeq)
		if res{
			t24Msg[count]=dat
			count++
		}
	}
	chanT24ToPri[nodeSeq]=make(chan messageT24,2*N/3+1)
}

func getT24Msg1(nodeSeq int,t24Msg *[2*N/3+1]messageT24){
	count :=0
	for dat:=range chanT24ToPri_1[nodeSeq]{
		var data=make([][]byte,2)
		data[0],_=json.Marshal(dat.prepare1)
		data[1],_=json.Marshal(dat.commit1)
		b:=bytes.Join(data,[]byte(""))
		res:=function.VerifySignHash(sha256.Sum256(b),dat.sign,dat.nodeSeq)
		if res{
			t24Msg[count]=dat
			count++
		}
	}
	chanT24ToPri_1[nodeSeq]=make(chan messageT24,2*N/3+1)
}


func primaryConsensus(priSeq int,flag chan bool,T5 int,T3 int,T1 int){
	var t135Msg messageT135
	var t135Msg3 messageT135
	var t135Msg5 messageT135
	var t24Msg[2*N/3+1] messageT24
	var flagT5=make(chan bool,1)
	var flagT3=make(chan bool,1)
	var flagT1=make(chan bool,1)
	if T5>=0{
		if T5%2==0{
				t135Msg.pre_prepare= pre_prepare(priSeq)
				flagT5<-true
				close(flagT5)
		}else {
				t135Msg.pre_prepare= pre_prepare1(priSeq)
				flagT5<-true
				close(flagT5)
		}
	}else {
		flagT5<-false
		close(flagT5)
	}
	if T3>=0{
		if T3%2==0{
				for i:=0;i<N;i++{
					<-FlagT24ed[i][priSeq]
				}
				close(chanT24ToPri[priSeq])
				getT24Msg(priSeq,&t24Msg)
				t135Msg3.prepare2.T24Set1,t135Msg3.prepare2.tradeHash=priGetMessageFromT24(&t24Msg,T3,priSeq)
				t135Msg3.prepare2.priSeq=priSeq
				flagT3<-true
				close(flagT3)
		}else {
			for i:=0;i<N;i++{
				<-FlagT24_1ed[i][priSeq]
			}
			close(chanT24ToPri_1[priSeq])
			getT24Msg1(priSeq,&t24Msg)
			t135Msg3.prepare2.T24Set1,t135Msg3.prepare2.tradeHash=priGetMessageFromT24(&t24Msg,T3,priSeq)
			t135Msg3.prepare2.priSeq=priSeq
			flagT3<-true
			close(flagT3)
		}
	}else {
		flagT3<-false
		close(flagT3)
	}
	if T1>=0{
				commit2(priSeq,T1,&t135Msg5.commit2)
				flagT1<-true
				close(flagT1)
	}else {
		flagT1<-false
		close(flagT1)
	}
	<-flagT5
	<-flagT3
	<-flagT1
	t135Msg.prepare2=t135Msg3.prepare2
	t135Msg.commit2=t135Msg5.commit2
	t135Msg.nodeSeq=priSeq
	var data=make([][]byte,3)
	data[0],_=json.Marshal(t135Msg.pre_prepare)
	data[1],_=json.Marshal(t135Msg.prepare2)
	data[2],_=json.Marshal(t135Msg.commit2)
	b:=bytes.Join(data,[]byte(""))
	t135Msg.sign=function.SignHash(sha256.Sum256(b),priSeq)
	if T5%2==0{
		sendT135Msg(priSeq,&t135Msg)
		if T1>=0{
			sendCommit2ToNode(priSeq,&t135Msg.commit2)
		}
	}else {
		sendT135Msg1(priSeq,&t135Msg)
		if T1>=0 {
			sendCommit2ToNode1(priSeq, &t135Msg.commit2)
		}
	}
	flag<-true
	close(flag)
}
func replicaConsensus(nodeSeq int,flag chan bool,T4 int,T2 int){
	var t24 messageT24
	var pre_prepare [K]pre_prepareMessage
	var prepare2 [K]prepare2Message
	var commit2[K]commit2Message
	if T4>=0{
		if T4%2==0{
			for i:=0;i<K;i++{
				<-FlagT135ed[primarysSeq[i]][nodeSeq]
			}
			close(chanT135ToNode[nodeSeq])
			getT135Msg(nodeSeq,&pre_prepare,&prepare2,&commit2)
		}else {
			for i:=0;i<K;i++{
				<-FlagT135_1ed[primarysSeq[i]][nodeSeq]
			}
			close(chanT135ToNode_1[nodeSeq])
			getT135Msg1(nodeSeq,&pre_prepare,&prepare2,&commit2)
		}
		t24.prepare1=prepare1(nodeSeq,&pre_prepare,T4)
	}

	if T2>=0{
		t24.commit1=commit1(nodeSeq,&prepare2,T2)
	}

	if T4>=0{
		if T4%2==0{
			var data=make([][]byte,2)
			data[0],_=json.Marshal(t24.prepare1)
			data[1],_=json.Marshal(t24.commit1)
			b:=bytes.Join(data,[]byte(""))
			t24.nodeSeq=nodeSeq
			t24.sign=function.SignHash(sha256.Sum256(b),nodeSeq)
			sendT24Msg(nodeSeq,&t24)
		}else {
			var data=make([][]byte,2)
			data[0],_=json.Marshal(t24.prepare1)
			data[1],_=json.Marshal(t24.commit1)
			b:=bytes.Join(data,[]byte(""))
			t24.nodeSeq=nodeSeq
			t24.sign=function.SignHash(sha256.Sum256(b),nodeSeq)
			sendT24Msg1(nodeSeq,&t24)
		}
	}

	flag<-true
	close(flag)
}


func channelInitial() {
	updatePrimarys()
	for i:=0;i<N;i++{
		chanT135ToNode[i]=make(chan messageT135,K)
		chanT135ToNode_1[i]=make(chan messageT135,K)
	}
	for i:=0;i<K;i++{
		chanT24ToPri[primarysSeq[i]]=make(chan messageT24,2*N/3+1)
		chanT24ToPri_1[primarysSeq[i]]=make(chan messageT24,2*N/3+1)
		FlagReplied[primarysSeq[i]]=make(chan bool,1)
		FlagReplied_1[primarysSeq[i]]=make(chan bool,1)
	}

	for i:=range primarysSeq {
		FlagT135ed[primarysSeq[i]]= make([]chan bool, N)
		FlagT135_1ed[primarysSeq[i]]= make([]chan bool, N)
	}
	for i:=range primarysSeq {
		for j:=0;j<N;j++{
			FlagT135ed[primarysSeq[i]][j]=make(chan bool,1)
			FlagT135_1ed[primarysSeq[i]][j]=make(chan bool,1)
		}
	}
	for i:=0;i<N;i++{
		FlagT24ed[i]=make([]chan bool,N)
		FlagT24_1ed[i]=make([]chan bool,N)
	}
	for i:=0;i<N;i++{
		for j:=0;j<N;j++{//到K即可
			FlagT24ed[i][j]=make(chan bool,1)
			FlagT24_1ed[i][j]=make(chan bool,1)
		}
	}
	chanReply=make(chan commit2Message,K)
}


func setVaildTurn(){
	for i:=0;i<N;i++{
		node[i].VaildTurn = math.Pow(float64(alpha),float64(node[i].ByzantineTurnNum))*float64(node[i].TurnNum)+float64(node[i].PriorNum)
	}
}

func priorNodeSet()  {
	for i:=0;i<K;i++{
		node[i].PriorNum=prior
	}
	for i:=0;i<K;i++{
		node[i].TurnNum=0
		node[i].ByzantineTurnNum=0
	}
	setVaildTurn()
	initialNode=true
}

func updatePrimarys(){
	if initialNode{
		setVaildTurn()
		var nodeSeq [K] int
		for i:=0;i<K;i++{
			max:=0.0
			maxSeq :=0
			for j:=0;j<N;j++{
				if node[j].VaildTurn>max {
					maxSeq=j
					max=node[j].VaildTurn
				}
			}
			nodeSeq[i]=maxSeq
			node[maxSeq].VaildTurn=-1.0
		}
		primarysSeq =nodeSeq
		backupsseqJ :=0
		for i:=0;i<N;i++{
			if node[i].VaildTurn !=-1.0{
				replicasSeq[backupsseqJ]=i
				backupsseqJ++
			}
		}
		setVaildTurn()
	}else{
		priorNodeSet()
		updatePrimarys()
	}
}
